home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 002 / dbug / dbug.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  35KB  |  1,471 lines

  1. /************************************************************************
  2.  *                                    *
  3.  *            Copyright (c) 1984, Fred Fish            *
  4.  *                All Rights Reserved                *
  5.  *                                    *
  6.  *    This software and/or documentation is released into the        *
  7.  *    public domain for personal, non-commercial use only.        *
  8.  *    Limited rights to use, modify, and redistribute are hereby    *
  9.  *    granted for non-commercial purposes, provided that all        *
  10.  *    copyright notices remain intact and all changes are clearly    *
  11.  *    documented.  The author makes no warranty of any kind with    *
  12.  *    respect to this product and explicitly disclaims any implied    *
  13.  *    warranties of merchantability or fitness for any particular    *
  14.  *    purpose.                            *
  15.  *                                    *
  16.  ************************************************************************
  17.  */
  18.  
  19.  
  20. /*
  21.  *  FILE
  22.  *
  23.  *    dbug.c   runtime support routines for dbug package
  24.  *
  25.  *  SCCS
  26.  *
  27.  *    @(#)dbug.c    1.12 12/20/85
  28.  *
  29.  *  DESCRIPTION
  30.  *
  31.  *    These are the runtime support routines for the dbug package.
  32.  *    The dbug package has two main components; the user include
  33.  *    file containing various macro definitions, and the runtime
  34.  *    support routines which are called from the macro expansions.
  35.  *
  36.  *    Externally visible functions in the runtime support module
  37.  *    use the naming convention pattern "_db_xx...xx_", thus
  38.  *    they are unlikely to collide with user defined function names.
  39.  *
  40.  *  AUTHOR
  41.  *
  42.  *    Fred Fish
  43.  *    (Currently at UniSoft Systems, Berkeley Ca.)
  44.  *
  45.  */
  46.  
  47.  
  48. #include <stdio.h>
  49.  
  50. /*
  51.  *    Manifest constants that should not require any changes.
  52.  */
  53.  
  54. #define FALSE        0    /* Boolean FALSE */
  55. #define TRUE        1    /* Boolean TRUE */
  56. #define EOS        '\000'    /* End Of String marker */
  57.  
  58. /*
  59.  *    Manifest constants which may be "tuned" if desired.
  60.  */
  61.  
  62. #define PRINTBUF    1024    /* Print buffer size */
  63. #define INDENT        4    /* Indentation per trace level */
  64. #define MAXDEPTH    200    /* Maximum trace depth default */
  65.  
  66. /*
  67.  *    The following flags are used to determine which
  68.  *    capabilities the user has enabled with the state
  69.  *    push macro.
  70.  */
  71.  
  72. #define TRACE_ON    000001    /* Trace enabled */
  73. #define DEBUG_ON    000002    /* Debug enabled */
  74. #define FILE_ON     000004    /* File name print enabled */
  75. #define LINE_ON        000010    /* Line number print enabled */
  76. #define DEPTH_ON    000020    /* Function nest level print enabled */
  77. #define PROCESS_ON    000040    /* Process name print enabled */
  78. #define NUMBER_ON    000100    /* Number each line of output */
  79.  
  80. #define TRACING (stack -> flags & TRACE_ON)
  81. #define DEBUGGING (stack -> flags & DEBUG_ON)
  82. #define STREQ(a,b) (strcmp(a,b) == 0)
  83.  
  84. /*
  85.  *    Typedefs to make things more obvious.
  86.  */
  87.  
  88. #define VOID void        /* Can't use typedef for most compilers */
  89. typedef int BOOLEAN;
  90.  
  91. /*
  92.  *    Make it easy to change storage classes if necessary.
  93.  */
  94.  
  95. #define LOCAL static        /* Names not needed by outside world */
  96. #define IMPORT extern        /* Names defined externally */
  97. #define EXPORT            /* Allocated here, available globally */
  98. #define AUTO auto        /* Names to be allocated on stack */
  99. #define REGISTER register    /* Names to be placed in registers */
  100.  
  101. /*
  102.  *    Variables which are available externally but should only
  103.  *    be accessed via the macro package facilities.
  104.  */
  105.  
  106. EXPORT FILE *_db_fp_ = stderr;        /* Output stream, default stderr */
  107. EXPORT char *_db_process_ = "dbug";    /* Pointer to process name; argv[0] */
  108. EXPORT BOOLEAN _db_on_ = FALSE;        /* TRUE if debugging currently on */
  109.  
  110. /*
  111.  *    Externally supplied functions.
  112.  */
  113.  
  114. #ifdef unix            /* Only needed for unix */
  115. IMPORT VOID perror ();        /* Print system/library error */
  116. IMPORT int chown ();        /* Change owner of a file */
  117. IMPORT int getgid ();        /* Get real group id */
  118. IMPORT int getuid ();        /* Get real user id */
  119. IMPORT int access ();        /* Test file for access */
  120. #else
  121. LOCAL VOID perror ();        /* Fake system/library error print routine */
  122. #endif
  123.  
  124. IMPORT int fprintf ();        /* Formatted print on file */
  125. IMPORT char *strcpy ();        /* Copy strings around */
  126. IMPORT int strlen ();        /* Find length of string */
  127. IMPORT char *malloc ();        /* Allocate memory */
  128. IMPORT int atoi ();        /* Convert ascii to integer */
  129.  
  130. #ifndef fflush            /* This is sometimes a macro */
  131.   IMPORT int fflush ();        /* Flush output for stream */
  132. #endif
  133.  
  134.  
  135. /*
  136.  *    The user may specify a list of functions to trace or 
  137.  *    debug.  These lists are kept in a linear linked list,
  138.  *    a very simple implementation.
  139.  */
  140.  
  141. struct link {
  142.     char *string;        /* Pointer to link's contents */
  143.     struct link *next_link;    /* Pointer to the next link */
  144. };
  145.  
  146.  
  147. /*
  148.  *    Debugging states can be pushed or popped off of a
  149.  *    stack which is implemented as a linked list.  Note
  150.  *    that the head of the list is the current state and the
  151.  *    stack is pushed by adding a new state to the head of the
  152.  *    list or popped by removing the first link.
  153.  */
  154.  
  155. struct state {
  156.     int flags;                /* Current state flags */
  157.     int maxdepth;            /* Current maximum trace depth */
  158.     int delay;                /* Delay after each output line */
  159.     int level;                /* Current function nesting level */
  160.     FILE *out_file;            /* Current output stream */
  161.     struct link *functions;        /* List of functions */
  162.     struct link *keywords;        /* List of debug keywords */
  163.     struct link *processes;        /* List of process names */
  164.     struct state *next_state;        /* Next state in the list */
  165. };
  166.  
  167. LOCAL struct state *stack = NULL;    /* Linked list of stacked states */
  168.  
  169. /*
  170.  *    Local variables not seen by user.
  171.  */
  172.  
  173. LOCAL int lineno = 0;        /* Current debugger output line number */
  174. LOCAL char *func = "?func";    /* Name of current user function */
  175. LOCAL char *file = "?file";    /* Name of current user file */
  176. LOCAL BOOLEAN init_done = FALSE;/* Set to TRUE when initialization done */
  177.  
  178. #if unix || AMIGA
  179. LOCAL int jmplevel;        /* Remember nesting level at setjmp () */
  180. LOCAL char *jmpfunc;        /* Remember current function for setjmp */
  181. LOCAL char *jmpfile;        /* Remember current file for setjmp */
  182. #endif
  183.  
  184. LOCAL struct link *ListParse ();/* Parse a debug command string */
  185. LOCAL char *StrDup ();        /* Make a fresh copy of a string */
  186. LOCAL VOID OpenFile ();        /* Open debug output stream */
  187. LOCAL VOID CloseFile ();    /* Close debug output stream */
  188. LOCAL VOID PushState ();    /* Push current debug state */
  189. LOCAL VOID ChangeOwner ();    /* Change file owner and group */
  190. LOCAL BOOLEAN DoTrace ();    /* Test for tracing enabled */
  191. LOCAL BOOLEAN Writable ();    /* Test to see if file is writable */
  192. LOCAL char *DbugMalloc ();    /* Allocate memory for runtime support */
  193. LOCAL char *BaseName ();    /* Remove leading pathname components */
  194.  
  195.                 /* Supplied in Sys V runtime environ */
  196. LOCAL char *strtok ();        /* Break string into tokens */
  197. LOCAL char *strrchr ();        /* Find last occurance of char */
  198.  
  199. /*
  200.  *    Miscellaneous printf format strings.
  201.  */
  202.  
  203. #define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
  204. #define ERR_OPEN "%s: can't open debug output stream \"%s\": "
  205. #define ERR_CLOSE "%s: can't close debug file: "
  206. #define ERR_ABORT "%s: debugger aborting because %s\n"
  207. #define ERR_CHOWN "%s: can't change owner/group of \"%s\": "
  208.  
  209. /*
  210.  *    Macros and defines for testing file accessibility under UNIX.
  211.  */
  212.  
  213. #ifdef unix
  214. #  define A_EXISTS    00        /* Test for file existance */
  215. #  define A_EXECUTE    01        /* Test for execute permission */
  216. #  define A_WRITE    02        /* Test for write access */
  217. #  define A_READ    03        /* Test for read access */
  218. #  define EXISTS(pathname) (access (pathname, A_EXISTS) == 0)
  219. #  define WRITABLE(pathname) (access (pathname, A_WRITE) == 0)
  220. #else
  221. #  define EXISTS(pathname) (FALSE)    /* Assume no existance */
  222. #endif
  223.  
  224. /*
  225.  *    Translate some calls among different systems.
  226.  */
  227.  
  228. #ifdef unix
  229. # define Delay sleep
  230. IMPORT unsigned sleep ();    /* Pause for given number of seconds */
  231. #endif
  232.  
  233. #ifdef AMIGA
  234. IMPORT int Delay ();        /* Pause for given number of ticks */
  235. #endif
  236.  
  237.  
  238. /*
  239.  *  FUNCTION
  240.  *
  241.  *    _db_push_    push current debugger state and set up new one
  242.  *
  243.  *  SYNOPSIS
  244.  *
  245.  *    VOID _db_push_ (control)
  246.  *    char *control;
  247.  *
  248.  *  DESCRIPTION
  249.  *
  250.  *    Given pointer to a debug control string in "control", pushes
  251.  *    the current debug state, parses the control string, and sets
  252.  *    up a new debug state.
  253.  *
  254.  *    The only attribute of the new state inherited from the previous
  255.  *    state is the current function nesting level.  This can be
  256.  *    overridden by using the "r" flag in the control string.
  257.  *
  258.  *    The debug control string is a sequence of colon separated fields
  259.  *    as follows:
  260.  *
  261.  *        <field_1>:<field_2>:...:<field_N>
  262.  *
  263.  *    Each field consists of a mandatory flag character followed by
  264.  *    an optional "," and comma separated list of modifiers:
  265.  *
  266.  *        flag[,modifier,modifier,...,modifier]
  267.  *
  268.  *    The currently recognized flag characters are:
  269.  *
  270.  *        d    Enable output from DBUG_<N> macros for
  271.  *            for the current state.  May be followed
  272.  *            by a list of keywords which selects output
  273.  *            only for the DBUG macros with that keyword.
  274.  *            A null list of keywords implies output for
  275.  *            all macros.
  276.  *
  277.  *        f    Limit debugging and/or tracing to the
  278.  *            list of named functions.  Note that a null
  279.  *            list will disable all functions.  The
  280.  *            appropriate "d" or "t" flags must still
  281.  *            be given, this flag only limits their
  282.  *            actions if they are enabled.
  283.  *
  284.  *        F    Identify the source file name for each
  285.  *            line of debug or trace output.
  286.  *
  287.  *        L    Identify the source file line number for
  288.  *            each line of debug or trace output.
  289.  *
  290.  *        n    Print the current function nesting depth for
  291.  *            each line of debug or trace output.
  292.  *    
  293.  *        N    Number each line of dbug output.
  294.  *
  295.  *        p    Limit debugger actions to specified processes.
  296.  *            A process must be identified with the
  297.  *            DBUG_PROCESS macro and match one in the list
  298.  *            for debugger actions to occur.
  299.  *
  300.  *        P    Print the current process name for each
  301.  *            line of debug or trace output.
  302.  *
  303.  *        r    When pushing a new state, do not inherit
  304.  *            the previous state's function nesting level.
  305.  *            Useful when the output is to start at the
  306.  *            left margin.
  307.  *
  308.  *        t    Enable function call/exit trace lines.
  309.  *            May be followed by a list (containing only
  310.  *            one modifier) giving a numeric maximum
  311.  *            trace level, beyond which no output will
  312.  *            occur for either debugging or tracing
  313.  *            macros.  The default is a compile time
  314.  *            option.
  315.  *
  316.  *    Some examples of debug control strings which might appear
  317.  *    on a shell command line (the "-#" is typically used to
  318.  *    introduce a control string to an application program) are:
  319.  *
  320.  *        -#d:t
  321.  *        -#d:f,main,subr1:F:L:t,20
  322.  *        -#d,input,output,files:n
  323.  *
  324.  *    For convenience, any leading "-#" is stripped off.
  325.  *
  326.  */
  327.  
  328.  
  329. VOID _db_push_ (control)
  330. char *control;
  331. {
  332.     REGISTER char *scan;
  333.     REGISTER struct link *temp;
  334.  
  335.     if (control && *control == '-') {
  336.     if (*++control == '#') {
  337.         control++;
  338.     }    
  339.     }
  340.     control = StrDup (control);
  341.     PushState ();
  342.     scan = strtok (control, ":");
  343.     for (; scan != NULL; scan = strtok ((char *)NULL, ":")) {
  344.     switch (*scan++) {
  345.         case 'd': 
  346.         _db_on_ = TRUE;
  347.         stack -> flags |= DEBUG_ON;
  348.         if (*scan++ == ',') {
  349.             stack -> keywords = ListParse (scan);
  350.         }
  351.         break;
  352.         case 'D': 
  353.         stack -> delay = 0;
  354.         if (*scan++ == ',') {
  355.             temp = ListParse (scan);
  356.             stack -> delay = DelayArg (atoi (temp -> string));
  357.             FreeList (temp);
  358.         }
  359.         break;
  360.         case 'f': 
  361.         if (*scan++ == ',') {
  362.             stack -> functions = ListParse (scan);
  363.         }
  364.         break;
  365.         case 'F': 
  366.         stack -> flags |= FILE_ON;
  367.         break;
  368.         case 'L': 
  369.         stack -> flags |= LINE_ON;
  370.         break;
  371.         case 'n': 
  372.         stack -> flags |= DEPTH_ON;
  373.         break;
  374.         case 'N':
  375.         stack -> flags |= NUMBER_ON;
  376.         break;
  377.         case 'o': 
  378.         if (*scan++ == ',') {
  379.             temp = ListParse (scan);
  380.             OpenFile (temp -> string);
  381.             FreeList (temp);
  382.         } else {
  383.             OpenFile ("-");
  384.         }
  385.         break;
  386.         case 'p':
  387.         if (*scan++ == ',') {
  388.             stack -> processes = ListParse (scan);
  389.         }
  390.         break;
  391.         case 'P': 
  392.         stack -> flags |= PROCESS_ON;
  393.         break;
  394.         case 'r': 
  395.         stack -> level = 0;
  396.         break;
  397.         case 't': 
  398.         stack -> flags |= TRACE_ON;
  399.         if (*scan++ == ',') {
  400.             temp = ListParse (scan);
  401.             stack -> maxdepth = atoi (temp -> string);
  402.             FreeList (temp);
  403.         }
  404.         break;
  405.     }
  406.     }
  407.     free (control);
  408. }
  409.  
  410.  
  411.  
  412. /*
  413.  *  FUNCTION
  414.  *
  415.  *    _db_pop_    pop the debug stack
  416.  *
  417.  *  DESCRIPTION
  418.  *
  419.  *    Pops the debug stack, returning the debug state to its
  420.  *    condition prior to the most recent _db_push_ invocation.
  421.  *    Note that the pop will fail if it would remove the last
  422.  *    valid state from the stack.  This prevents user errors
  423.  *    in the push/pop sequence from screwing up the debugger.
  424.  *    Maybe there should be some kind of warning printed if the
  425.  *    user tries to pop too many states.
  426.  *
  427.  */
  428.  
  429. VOID _db_pop_ ()
  430. {
  431.     REGISTER struct state *discard;
  432.  
  433.     discard = stack;
  434.     if (discard != NULL && discard -> next_state != NULL) {
  435.     stack = discard -> next_state;
  436.     _db_fp_ = stack -> out_file;
  437.     if (discard -> keywords != NULL) {
  438.         FreeList (discard -> keywords);
  439.     }
  440.     if (discard -> functions != NULL) {
  441.         FreeList (discard -> functions);
  442.     }
  443.     if (discard -> processes != NULL) {
  444.         FreeList (discard -> processes);
  445.     }
  446.     CloseFile (discard -> out_file);
  447.     free ((char *) discard);
  448.     }
  449. }
  450.  
  451.  
  452. /*
  453.  *  FUNCTION
  454.  *
  455.  *    _db_enter_    process entry point to user function
  456.  *
  457.  *  SYNOPSIS
  458.  *
  459.  *    VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_)
  460.  *    char *_func_;        points to current function name
  461.  *    char *_file_;        points to current file name
  462.  *    int _line_;        called from source line number
  463.  *    char **_sfunc_;        save previous _func_
  464.  *    char **_sfile_;        save previous _file_
  465.  *    int *_slevel_;        save previous nesting level
  466.  *
  467.  *  DESCRIPTION
  468.  *
  469.  *    Called at the beginning of each user function to tell
  470.  *    the debugger that a new function has been entered.
  471.  *    Note that the pointers to the previous user function
  472.  *    name and previous user file name are stored on the
  473.  *    caller's stack (this is why the ENTER macro must be
  474.  *    the first "executable" code in a function, since it
  475.  *    allocates these storage locations).  The previous nesting
  476.  *    level is also stored on the callers stack for internal
  477.  *    self consistency checks.
  478.  *
  479.  *    Also prints a trace line if tracing is enabled and
  480.  *    increments the current function nesting depth.
  481.  *
  482.  *    Note that this mechanism allows the debugger to know
  483.  *    what the current user function is at all times, without
  484.  *    maintaining an internal stack for the function names.
  485.  *
  486.  */
  487.  
  488. VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_)
  489. char *_func_;
  490. char *_file_;
  491. int _line_;
  492. char **_sfunc_;
  493. char **_sfile_;
  494. int *_slevel_;
  495. {
  496.     if (!init_done) {
  497.     _db_push_ ("");
  498.     }
  499.     *_sfunc_ = func;
  500.     *_sfile_ = file;
  501.     func = _func_;
  502.     file = BaseName (_file_);
  503.     stack -> level++;
  504.     *_slevel_ = stack -> level;
  505.     if (DoTrace ()) {
  506.     DoPrefix (_line_);
  507.     Indent (stack -> level);
  508.     (VOID) fprintf (_db_fp_, ">%s\n", func);
  509.     (VOID) fflush (_db_fp_);
  510.     (VOID) Delay (stack -> delay);
  511.     }
  512. }
  513.  
  514.  
  515. /*
  516.  *  FUNCTION
  517.  *
  518.  *    _db_return_    process exit from user function
  519.  *
  520.  *  SYNOPSIS
  521.  *
  522.  *    VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  523.  *    int _line_;        current source line number
  524.  *    char **_sfunc_;        where previous _func_ is to be retrieved
  525.  *    char **_sfile_;        where previous _file_ is to be retrieved
  526.  *    int *_slevel_;        where previous level was stashed
  527.  *
  528.  *  DESCRIPTION
  529.  *
  530.  *    Called just before user function executes an explicit or implicit
  531.  *    return.  Prints a trace line if trace is enabled, decrements
  532.  *    the current nesting level, and restores the current function and
  533.  *    file names from the defunct function's stack.
  534.  *
  535.  */
  536.  
  537. VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  538. int _line_;
  539. char **_sfunc_;
  540. char **_sfile_;
  541. int *_slevel_;
  542. {
  543.     if (!init_done) {
  544.     _db_push_ ("");
  545.     }
  546.     if (stack -> level != *_slevel_ && (TRACING || DEBUGGING)) {
  547.     (VOID) fprintf (_db_fp_, ERR_MISSING_RETURN, _db_process_, func);
  548.     (VOID) fflush (_db_fp_);
  549.     } else if (DoTrace ()) {
  550.     DoPrefix (_line_);
  551.     Indent (stack -> level);
  552.     (VOID) fprintf (_db_fp_, "<%s\n", func);
  553.     (VOID) fflush (_db_fp_);
  554.     }
  555.     (VOID) Delay (stack -> delay);
  556.     stack -> level = *_slevel_ - 1;
  557.     func = *_sfunc_;
  558.     file = *_sfile_;
  559. }
  560.  
  561.  
  562. /*
  563.  *  FUNCTION
  564.  *
  565.  *    _db_printf_    handle print of debug lines
  566.  *
  567.  *  SYNOPSIS
  568.  *
  569.  *    VOID _db_printf_ (_line_, keyword, format,
  570.  *        a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)
  571.  *    int _line_;
  572.  *    char *keyword,  *format;
  573.  *    int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11;
  574.  *
  575.  *  DESCRIPTION
  576.  *
  577.  *    When invoked via one of the DBUG macros, tests the keyword
  578.  *    to see if that macro has been selected for processing via
  579.  *    the debugger control string, and if so, handles printing
  580.  *    of the arguments via the format string.  The line number
  581.  *    of the DBUG macro in the source is found in _line_.
  582.  *
  583.  *    Note that the format string SHOULD NOT include a terminating
  584.  *    newline, this is supplied automatically.
  585.  *
  586.  *  NOTE
  587.  *
  588.  *    The rather ugly argument declaration is to handle some
  589.  *    magic with respect to the number of arguments passed
  590.  *    via the DBUG macros.  The current maximum is 3 arguments
  591.  *    (not including the keyword and format strings).
  592.  *
  593.  *    If the args being passed by the DBUG macro are actually
  594.  *    doubles (worst case) then there will be a total of 12
  595.  *    ints on the stack for a PDP-11 or 6 ints on a 68000.
  596.  *
  597.  */
  598.  
  599. /*VARARGS3*/
  600. VOID _db_printf_ (_line_, keyword, format, 
  601.     a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)
  602. int _line_;
  603. char *keyword,  *format;
  604. int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11;
  605. {
  606.     if (_db_keyword_ (keyword)) {
  607.     DoPrefix (_line_);
  608.     if (TRACING) {
  609.         Indent (stack -> level + 1);
  610.     } else {
  611.         (VOID) fprintf (_db_fp_, "%s: ", func);
  612.     }
  613.     (VOID) fprintf (_db_fp_, "%s: ", keyword);
  614.     (VOID) fprintf (_db_fp_, format, a0, a1, a2, a3, a4, a5, a6,a7, a8,
  615.         a9, a10, a11);
  616.     (VOID) fprintf (_db_fp_, "\n");
  617.     (VOID) fflush (_db_fp_);
  618.     (VOID) Delay (stack -> delay);
  619.     }
  620. }
  621.  
  622.  
  623. /*
  624.  *  FUNCTION
  625.  *
  626.  *    ListParse    parse list of modifiers in debug control string
  627.  *
  628.  *  SYNOPSIS
  629.  *
  630.  *    LOCAL struct link *ListParse (ctlp)
  631.  *    char *ctlp;
  632.  *
  633.  *  DESCRIPTION
  634.  *
  635.  *    Given pointer to a comma separated list of strings in "cltp",
  636.  *    parses the list, building a list and returning a pointer to it.
  637.  *    The original comma separated list is destroyed in the process of
  638.  *    building the linked list, thus it had better be a duplicate
  639.  *    if it is important.
  640.  *
  641.  *    Note that since each link is added at the head of the list,
  642.  *    the final list will be in "reverse order", which is not
  643.  *    significant for our usage here.
  644.  *
  645.  */
  646.  
  647. LOCAL struct link *ListParse (ctlp)
  648. char *ctlp;
  649. {
  650.     REGISTER char *start;
  651.     REGISTER struct link *new;
  652.     REGISTER struct link *head;
  653.  
  654.     head = NULL;
  655.     while (*ctlp != EOS) {
  656.     start = ctlp;
  657.     while (*ctlp != EOS && *ctlp != ',') {
  658.         ctlp++;
  659.     }
  660.     if (*ctlp == ',') {
  661.         *ctlp++ = EOS;
  662.     }
  663.     new = (struct link *) DbugMalloc (sizeof (struct link));
  664.     new -> string = StrDup (start);
  665.     new -> next_link = head;
  666.     head = new;
  667.     }
  668.     return (head);
  669. }
  670.  
  671.  
  672. /*
  673.  *  FUNCTION
  674.  *
  675.  *    InList    test a given string for member of a given list
  676.  *
  677.  *  SYNOPSIS
  678.  *
  679.  *    LOCAL BOOLEAN InList (linkp, cp)
  680.  *    struct link *linkp;
  681.  *    char *cp;
  682.  *
  683.  *  DESCRIPTION
  684.  *
  685.  *    Tests the string pointed to by "cp" to determine if it is in
  686.  *    the list pointed to by "linkp".  Linkp points to the first
  687.  *    link in the list.  If linkp is NULL then the string is treated
  688.  *    as if it is in the list (I.E all strings are in the null list).
  689.  *    This may seem rather strange at first but leads to the desired
  690.  *    operation if no list is given.  The net effect is that all
  691.  *    strings will be accepted when there is no list, and when there
  692.  *    is a list, only those strings in the list will be accepted.
  693.  *
  694.  */
  695.  
  696. LOCAL BOOLEAN InList (linkp, cp)
  697. struct link *linkp;
  698. char *cp;
  699. {
  700.     REGISTER struct link *scan;
  701.     REGISTER BOOLEAN accept;
  702.  
  703.     if (linkp == NULL) {
  704.     accept = TRUE;
  705.     } else {
  706.     accept = FALSE;
  707.     for (scan = linkp; scan != NULL; scan = scan -> next_link) {
  708.         if (STREQ (scan -> string, cp)) {
  709.         accept = TRUE;
  710.         break;
  711.         }
  712.     }
  713.     }
  714.     return (accept);
  715. }
  716.  
  717.  
  718. /*
  719.  *  FUNCTION
  720.  *
  721.  *    PushState    push current state onto stack and set up new one
  722.  *
  723.  *  SYNOPSIS
  724.  *
  725.  *    LOCAL VOID PushState ()
  726.  *
  727.  *  DESCRIPTION
  728.  *
  729.  *    Pushes the current state on the state stack, and initializes
  730.  *    a new state.  The only parameter inherited from the previous
  731.  *    state is the function nesting level.  This action can be
  732.  *    inhibited if desired, via the "r" flag.
  733.  *
  734.  *    The state stack is a linked list of states, with the new
  735.  *    state added at the head.  This allows the stack to grow
  736.  *    to the limits of memory if necessary.
  737.  *
  738.  */
  739.  
  740. LOCAL VOID PushState ()
  741. {
  742.     REGISTER struct state *new;
  743.  
  744.     new = (struct state *) DbugMalloc (sizeof (struct state));
  745.     new -> flags = 0;
  746.     new -> delay = 0;
  747.     new -> maxdepth = MAXDEPTH;
  748.     if (stack != NULL) {
  749.     new -> level = stack -> level;
  750.     } else {
  751.     new -> level = 0;
  752.     }
  753.     new -> out_file = stderr;
  754.     new -> functions = NULL;
  755.     new -> keywords = NULL;
  756.     new -> processes = NULL;
  757.     new -> next_state = stack;
  758.     stack = new;
  759.     init_done = TRUE;
  760. }
  761.  
  762.  
  763. /*
  764.  *  FUNCTION
  765.  *
  766.  *    DoTrace    check to see if tracing is current enabled
  767.  *
  768.  *  SYNOPSIS
  769.  *
  770.  *    LOCAL BOOLEAN DoTrace ()
  771.  *
  772.  *  DESCRIPTION
  773.  *
  774.  *    Checks to see if tracing is enabled based on whether the
  775.  *    user has specified tracing, the maximum trace depth has
  776.  *    not yet been reached, the current function is selected,
  777.  *    and the current process is selected.  Returns TRUE if
  778.  *    tracing is enabled, FALSE otherwise.
  779.  *
  780.  */
  781.  
  782. LOCAL BOOLEAN DoTrace ()
  783. {
  784.     REGISTER BOOLEAN trace;
  785.  
  786.     trace = FALSE;
  787.     if (TRACING) {
  788.     if (stack -> level <= stack -> maxdepth) {
  789.         if (InList (stack -> functions, func)) {
  790.         if (InList (stack -> processes, _db_process_)) {
  791.             trace = TRUE;
  792.         }
  793.         }
  794.     }
  795.     }
  796.     return (trace);
  797. }
  798.  
  799.  
  800. /*
  801.  *  FUNCTION
  802.  *
  803.  *    _db_keyword_    test keyword for member of keyword list
  804.  *
  805.  *  SYNOPSIS
  806.  *
  807.  *    BOOLEAN _db_keyword_ (keyword)
  808.  *    char *keyword;
  809.  *
  810.  *  DESCRIPTION
  811.  *
  812.  *    Test a keyword to determine if it is in the currently active
  813.  *    keyword list.  As with the function list, a keyword is accepted
  814.  *    if the list is null, otherwise it must match one of the list
  815.  *    members.  When debugging is not on, no keywords are accepted.
  816.  *    After the maximum trace level is exceeded, no keywords are
  817.  *    accepted (this behavior subject to change).  Additionally,
  818.  *    the current function and process must be accepted based on
  819.  *    their respective lists.
  820.  *
  821.  *    Returns TRUE if keyword accepted, FALSE otherwise.
  822.  *
  823.  */
  824.  
  825. BOOLEAN _db_keyword_ (keyword)
  826. char *keyword;
  827. {
  828.     REGISTER BOOLEAN accept;
  829.  
  830.     if (!init_done) {
  831.     _db_push_ ("");
  832.     }
  833.     accept = FALSE;
  834.     if (DEBUGGING) {
  835.     if (stack -> level <= stack -> maxdepth) {
  836.         if (InList (stack -> functions, func)) {
  837.         if (InList (stack -> keywords, keyword)) {
  838.             if (InList (stack -> processes, _db_process_)) {
  839.             accept = TRUE;
  840.             }
  841.         }
  842.         }
  843.     }
  844.     }
  845.     return (accept);
  846. }
  847.  
  848.  
  849. /*
  850.  *  FUNCTION
  851.  *
  852.  *    Indent    indent a line to the given indentation level
  853.  *
  854.  *  SYNOPSIS
  855.  *
  856.  *    LOCAL Indent (indent)
  857.  *    int indent;
  858.  *
  859.  *  DESCRIPTION
  860.  *
  861.  *    Indent a line to the given level.  Note that this is
  862.  *    a simple minded but portable implementation.
  863.  *    There are better ways.
  864.  *
  865.  *    Also, the indent must be scaled by the compile time option
  866.  *    of character positions per nesting level.
  867.  *
  868.  */
  869.  
  870. LOCAL Indent (indent)
  871. int indent;
  872. {
  873.     REGISTER int count;
  874.     AUTO char buffer[PRINTBUF];
  875.  
  876.     indent *= INDENT;
  877.     for (count = 0; (count < (indent - INDENT)) && (count < (PRINTBUF - 1)); count++) {
  878.     if ((count % INDENT) == 0) {
  879.         buffer[count] = '|';
  880.     } else {
  881.         buffer[count] = ' ';
  882.     }
  883.     }
  884.     buffer[count] = EOS;
  885.     (VOID) fprintf (_db_fp_, buffer);
  886.     (VOID) fflush (_db_fp_);
  887. }
  888.  
  889.  
  890. /*
  891.  *  FUNCTION
  892.  *
  893.  *    FreeList    free all memory associated with a linked list
  894.  *
  895.  *  SYNOPSIS
  896.  *
  897.  *    LOCAL FreeList (linkp)
  898.  *    struct link *linkp;
  899.  *
  900.  *  DESCRIPTION
  901.  *
  902.  *    Given pointer to the head of a linked list, frees all
  903.  *    memory held by the list and the members of the list.
  904.  *
  905.  */
  906.  
  907. LOCAL FreeList (linkp)
  908. struct link *linkp;
  909. {
  910.     REGISTER struct link *old;
  911.  
  912.     while (linkp != NULL) {
  913.     old = linkp;
  914.     linkp = linkp -> next_link;
  915.     if (old -> string != NULL) {
  916.         free (old -> string);
  917.     }
  918.     free ((char *) old);
  919.     }
  920. }
  921.  
  922.  
  923. /*
  924.  *  FUNCTION
  925.  *
  926.  *    StrDup   make a duplicate of a string in new memory
  927.  *
  928.  *  SYNOPSIS
  929.  *
  930.  *    LOCAL char *StrDup (string)
  931.  *    char *string;
  932.  *
  933.  *  DESCRIPTION
  934.  *
  935.  *    Given pointer to a string, allocates sufficient memory to make
  936.  *    a duplicate copy, and copies the string to the newly allocated
  937.  *    memory.  Failure to allocated sufficient memory is immediately
  938.  *    fatal.
  939.  *
  940.  */
  941.  
  942.  
  943. LOCAL char *StrDup (string)
  944. char *string;
  945. {
  946.     REGISTER char *new;
  947.  
  948.     new = DbugMalloc (strlen (string) + 1);
  949.     (VOID) strcpy (new, string);
  950.     return (new);
  951. }
  952.  
  953.  
  954. /*
  955.  *  FUNCTION
  956.  *
  957.  *    DoPrefix    print debugger line prefix prior to indentation
  958.  *
  959.  *  SYNOPSIS
  960.  *
  961.  *    LOCAL DoPrefix (_line_)
  962.  *    int _line_;
  963.  *
  964.  *  DESCRIPTION
  965.  *
  966.  *    Print prefix common to all debugger output lines, prior to
  967.  *    doing indentation if necessary.  Print such information as
  968.  *    current process name, current source file name and line number,
  969.  *    and current function nesting depth.
  970.  *
  971.  */
  972.   
  973.  
  974. LOCAL DoPrefix (_line_)
  975. int _line_;
  976. {
  977.     lineno++;
  978.     if (stack -> flags & NUMBER_ON) {
  979.     (VOID) fprintf (_db_fp_, "%5d: ", lineno);
  980.     }
  981.     if (stack -> flags & PROCESS_ON) {
  982.     (VOID) fprintf (_db_fp_, "%s: ", _db_process_);
  983.     }
  984.     if (stack -> flags & FILE_ON) {
  985.     (VOID) fprintf (_db_fp_, "%14s: ", file);
  986.     }
  987.     if (stack -> flags & LINE_ON) {
  988.     (VOID) fprintf (_db_fp_, "%5d: ", _line_);
  989.     }
  990.     if (stack -> flags & DEPTH_ON) {
  991.     (VOID) fprintf (_db_fp_, "%4d: ", stack -> level);
  992.     }
  993.     (VOID) fflush (_db_fp_);
  994. }
  995.  
  996.  
  997. /*
  998.  *  FUNCTION
  999.  *
  1000.  *    OpenFile    open new output stream for debugger output
  1001.  *
  1002.  *  SYNOPSIS
  1003.  *
  1004.  *    LOCAL VOID OpenFile (name)
  1005.  *    char *name;
  1006.  *
  1007.  *  DESCRIPTION
  1008.  *
  1009.  *    Given name of a new file (or "-" for stdout) opens the file
  1010.  *    and sets the output stream to the new file.
  1011.  *
  1012.  */
  1013.  
  1014. LOCAL VOID OpenFile (name)
  1015. char *name;
  1016. {
  1017.     REGISTER FILE *fp;
  1018.     REGISTER BOOLEAN newfile;
  1019.  
  1020.     if (name != NULL) {
  1021.     if (strcmp (name, "-") == 0) {
  1022.         _db_fp_ = stdout;
  1023.         stack -> out_file = _db_fp_;
  1024.     } else {
  1025.         if (!Writable (name)) {
  1026.         (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1027.         perror ("");
  1028.         (VOID) fflush (_db_fp_);
  1029.         (VOID) Delay (stack -> delay);
  1030.         } else {
  1031.         if (EXISTS (name)) {
  1032.             newfile = FALSE;
  1033.         } else {
  1034.             newfile = TRUE;
  1035.         }
  1036.         fp = fopen (name, "a");
  1037.         if (fp == NULL) {
  1038.              (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1039.             perror ("");
  1040.             (VOID) fflush (_db_fp_);
  1041.             (VOID) Delay (stack -> delay);
  1042.         } else {
  1043.             _db_fp_ = fp;
  1044.             stack -> out_file = fp;
  1045.             if (newfile) {
  1046.             ChangeOwner (name);
  1047.             }
  1048.         }
  1049.         }
  1050.     }
  1051.     }
  1052. }
  1053.  
  1054.  
  1055. /*
  1056.  *  FUNCTION
  1057.  *
  1058.  *    CloseFile    close the debug output stream
  1059.  *
  1060.  *  SYNOPSIS
  1061.  *
  1062.  *    LOCAL VOID CloseFile (fp)
  1063.  *    FILE *fp;
  1064.  *
  1065.  *  DESCRIPTION
  1066.  *
  1067.  *    Closes the debug output stream unless it is standard output
  1068.  *    or standard error.
  1069.  *
  1070.  */
  1071.  
  1072. LOCAL VOID CloseFile (fp)
  1073. FILE *fp;
  1074. {
  1075.     if (fp != stderr && fp != stdout) {
  1076.     if (fclose (fp) == EOF) {
  1077.         (VOID) fprintf (stderr, ERR_CLOSE, _db_process_);
  1078.         perror ("");
  1079.         (VOID) fflush (stderr);
  1080.         (VOID) Delay (stack -> delay);
  1081.     }
  1082.     }
  1083. }
  1084.  
  1085.  
  1086. /*
  1087.  *  FUNCTION
  1088.  *
  1089.  *    DbugExit    print error message and exit
  1090.  *
  1091.  *  SYNOPSIS
  1092.  *
  1093.  *    LOCAL VOID DbugExit (why)
  1094.  *    char *why;
  1095.  *
  1096.  *  DESCRIPTION
  1097.  *
  1098.  *    Prints error message using current process name, the reason for
  1099.  *    aborting (typically out of memory), and exits with status 1.
  1100.  *    This should probably be changed to use a status code
  1101.  *    defined in the user's debugger include file.
  1102.  *
  1103.  */
  1104.  
  1105. LOCAL VOID DbugExit (why)
  1106. char *why;
  1107. {
  1108.     (VOID) fprintf (stderr, ERR_ABORT, _db_process_, why);
  1109.     (VOID) fflush (stderr);
  1110.     (VOID) Delay (stack -> delay);
  1111.     exit (1);
  1112. }
  1113.  
  1114.  
  1115. /*
  1116.  *  FUNCTION
  1117.  *
  1118.  *    DbugMalloc    allocate memory for debugger runtime support
  1119.  *
  1120.  *  SYNOPSIS
  1121.  *
  1122.  *    LOCAL char *DbugMalloc (size)
  1123.  *    int size;
  1124.  *
  1125.  *  DESCRIPTION
  1126.  *
  1127.  *    Allocate more memory for debugger runtime support functions.
  1128.  *    Failure to to allocate the requested number of bytes is
  1129.  *    immediately fatal to the current process.  This may be
  1130.  *    rather unfriendly behavior.  It might be better to simply
  1131.  *    print a warning message, freeze the current debugger state,
  1132.  *    and continue execution.
  1133.  *
  1134.  */
  1135.  
  1136. LOCAL char *DbugMalloc (size)
  1137. int size;
  1138. {
  1139.     register char *new;
  1140.  
  1141.     new = malloc ((unsigned int) size);
  1142.     if (new == NULL) {
  1143.     DbugExit ("out of memory");
  1144.     }
  1145.     return (new);
  1146. }
  1147.  
  1148.  
  1149. /*
  1150.  *    This function may be eliminated when strtok is available
  1151.  *    in the runtime environment (missing from BSD4.1).
  1152.  */
  1153.  
  1154. LOCAL char *strtok (s1, s2)
  1155. char *s1, *s2;
  1156. {
  1157.     static char *end = NULL;
  1158.     REGISTER char *rtnval;
  1159.  
  1160.     rtnval = NULL;
  1161.     if (s2 != NULL) {
  1162.     if (s1 != NULL) {
  1163.         end = s1;
  1164.         rtnval = strtok ((char *) NULL, s2);
  1165.     } else if (end != NULL) {
  1166.         if (*end != EOS) {
  1167.         rtnval = end;
  1168.         while (*end != *s2 && *end != EOS) {end++;}
  1169.         if (*end != EOS) {
  1170.             *end++ = EOS;
  1171.         }
  1172.         }
  1173.     }
  1174.     }
  1175.     return (rtnval);
  1176. }
  1177.  
  1178.  
  1179. /*
  1180.  *  FUNCTION
  1181.  *
  1182.  *    BaseName    strip leading pathname components from name
  1183.  *
  1184.  *  SYNOPSIS
  1185.  *
  1186.  *    LOCAL char *BaseName (pathname)
  1187.  *    char *pathname;
  1188.  *
  1189.  *  DESCRIPTION
  1190.  *
  1191.  *    Given pointer to a complete pathname, locates the base file
  1192.  *    name at the end of the pathname and returns a pointer to
  1193.  *    it.
  1194.  *
  1195.  */
  1196.  
  1197. LOCAL char *BaseName (pathname)
  1198. char *pathname;
  1199. {
  1200.     register char *base;
  1201.  
  1202.     base = strrchr (pathname, '/');
  1203.     if (base++ == NULL) {
  1204.     base = pathname;
  1205.     }
  1206.     return (base);
  1207. }
  1208.  
  1209.  
  1210. /*
  1211.  *  FUNCTION
  1212.  *
  1213.  *    Writable    test to see if a pathname is writable/creatable
  1214.  *
  1215.  *  SYNOPSIS
  1216.  *
  1217.  *    LOCAL BOOLEAN Writable (pathname)
  1218.  *    char *pathname;
  1219.  *
  1220.  *  DESCRIPTION
  1221.  *
  1222.  *    Because the debugger might be linked in with a program that
  1223.  *    runs with the set-uid-bit (suid) set, we have to be careful
  1224.  *    about opening a user named file for debug output.  This consists
  1225.  *    of checking the file for write access with the real user id,
  1226.  *    or checking the directory where the file will be created.
  1227.  *
  1228.  *    Returns TRUE if the user would normally be allowed write or
  1229.  *    create access to the named file.  Returns FALSE otherwise.
  1230.  *
  1231.  */
  1232.  
  1233. LOCAL BOOLEAN Writable (pathname)
  1234. char *pathname;
  1235. {
  1236.     REGISTER BOOLEAN granted;
  1237. #ifdef unix
  1238.     REGISTER char *lastslash;
  1239. #endif
  1240.  
  1241. #ifndef unix
  1242.     granted = TRUE;
  1243. #else
  1244.     granted = FALSE;
  1245.     if (EXISTS (pathname)) {
  1246.     if (WRITABLE (pathname)) {
  1247.         granted = TRUE;
  1248.     }
  1249.     } else {
  1250.     lastslash = strrchr (pathname, '/');
  1251.     if (lastslash != NULL) {
  1252.         *lastslash = EOS;
  1253.     } else {
  1254.         pathname = ".";
  1255.     }
  1256.     if (WRITABLE (pathname)) {
  1257.         granted = TRUE;
  1258.     }
  1259.     if (lastslash != NULL) {
  1260.         *lastslash = '/';
  1261.     }
  1262.     }
  1263. #endif
  1264.     return (granted);
  1265. }
  1266.  
  1267.  
  1268. /*
  1269.  *    This function may be eliminated when strrchr is available
  1270.  *    in the runtime environment (missing from BSD4.1).
  1271.  *    Alternately, you can use rindex() on BSD systems.
  1272.  */
  1273.  
  1274. LOCAL char *strrchr (s, c)
  1275. char *s;
  1276. char c;
  1277. {
  1278.     REGISTER char *scan;
  1279.  
  1280.     for (scan = s; *scan != EOS; scan++) {;}
  1281.     while (scan > s && *--scan != c) {;}
  1282.     if (*scan != c) {
  1283.     scan = NULL;
  1284.     }
  1285.     return (scan);
  1286. }
  1287.  
  1288.  
  1289. /*
  1290.  *  FUNCTION
  1291.  *
  1292.  *    ChangeOwner    change owner to real user for suid programs
  1293.  *
  1294.  *  SYNOPSIS
  1295.  *
  1296.  *    LOCAL VOID ChangeOwner (pathname)
  1297.  *
  1298.  *  DESCRIPTION
  1299.  *
  1300.  *    For unix systems, change the owner of the newly created debug
  1301.  *    file to the real owner.  This is strictly for the benefit of
  1302.  *    programs that are running with the set-user-id bit set.
  1303.  *
  1304.  *    Note that at this point, the fact that pathname represents
  1305.  *    a newly created file has already been established.  If the
  1306.  *    program that the debugger is linked to is not running with
  1307.  *    the suid bit set, then this operation is redundant (but
  1308.  *    harmless).
  1309.  *
  1310.  */
  1311.  
  1312. LOCAL VOID ChangeOwner (pathname)
  1313. char *pathname;
  1314. {
  1315. #ifdef unix
  1316.     if (chown (pathname, getuid (), getgid ()) == -1) {
  1317.     (VOID) fprintf (stderr, ERR_CHOWN, _db_process_, pathname);
  1318.     perror ("");
  1319.     (VOID) fflush (stderr);
  1320.     (VOID) Delay (stack -> delay);
  1321.     }
  1322. #endif
  1323. }
  1324.  
  1325.  
  1326. /*
  1327.  *  FUNCTION
  1328.  *
  1329.  *    _db_setjmp_    save debugger environment
  1330.  *
  1331.  *  SYNOPSIS
  1332.  *
  1333.  *    EXPORT void _db_setjmp_ ()
  1334.  *
  1335.  *  DESCRIPTION
  1336.  *
  1337.  *    Invoked as part of the user's DBUG_SETJMP macro to save
  1338.  *    the debugger environment in parallel with saving the user's
  1339.  *    environment.
  1340.  *
  1341.  */
  1342.  
  1343. EXPORT void _db_setjmp_ ()
  1344. {
  1345.    jmplevel = stack -> level;
  1346.    jmpfunc = func;
  1347.    jmpfile = file;
  1348. }
  1349.  
  1350.  
  1351. /*
  1352.  *  FUNCTION
  1353.  *
  1354.  *    _db_longjmp_    restore previously saved debugger environment
  1355.  *
  1356.  *  SYNOPSIS
  1357.  *
  1358.  *    EXPORT void _db_longjmp_ ()
  1359.  *
  1360.  *  DESCRIPTION
  1361.  *
  1362.  *    Invoked as part of the user's DBUG_LONGJMP macro to restore
  1363.  *    the debugger environment in parallel with restoring the user's
  1364.  *    previously saved environment.
  1365.  *
  1366.  */
  1367.  
  1368. EXPORT void _db_longjmp_ ()
  1369. {
  1370.     stack -> level = jmplevel;
  1371.     if (jmpfunc) {
  1372.     func = jmpfunc;
  1373.     }
  1374.     if (jmpfile) {
  1375.     file = jmpfile;
  1376.     }
  1377. }
  1378.  
  1379.  
  1380. /*
  1381.  *  FUNCTION
  1382.  *
  1383.  *    DelayArg   convert D flag argument to appropriate value
  1384.  *
  1385.  *  SYNOPSIS
  1386.  *
  1387.  *    LOCAL int DelayArg (value)
  1388.  *    int value;
  1389.  *
  1390.  *  DESCRIPTION
  1391.  *
  1392.  *    Converts delay argument, given in tenths of a second, to the
  1393.  *    appropriate numerical argument used by the system to delay
  1394.  *    that that many tenths of a second.  For example, on the
  1395.  *    AMIGA, there is a system call "Delay()" which takes an
  1396.  *    argument in ticks (50 per second).  On unix, the sleep
  1397.  *    command takes seconds.  Thus a value of "10", for one
  1398.  *    second of delay, gets converted to 50 on the amiga, and 1
  1399.  *    on unix.  Other systems will need to use a timing loop.
  1400.  *
  1401.  */
  1402.  
  1403. #ifdef AMIGA
  1404. #define HZ (50)            /* Probably in some header somewhere */
  1405. #endif
  1406.  
  1407. LOCAL int DelayArg (value)
  1408. int value;
  1409. {
  1410.     int delayarg = 0;
  1411.     
  1412. #ifdef unix
  1413.     delayarg = value / 10;        /* Delay is in seconds for sleep () */
  1414. #endif
  1415. #ifdef AMIGA
  1416.     delayarg = (HZ * value) / 10;    /* Delay in ticks for Delay () */
  1417. #endif
  1418.     return (delayarg);
  1419. }
  1420.  
  1421.  
  1422. /*
  1423.  *    A dummy delay stub for systems that do not support delays.
  1424.  *    With a little work, this can be turned into a timing loop.
  1425.  */
  1426.  
  1427. #ifndef unix
  1428. #ifndef AMIGA
  1429. Delay ()
  1430. {
  1431. }
  1432. #endif
  1433. #endif
  1434.  
  1435.  
  1436. /*
  1437.  *  FUNCTION
  1438.  *
  1439.  *    perror    perror simulation for systems that don't have it
  1440.  *
  1441.  *  SYNOPSIS
  1442.  *
  1443.  *    LOCAL VOID perror (s)
  1444.  *    char *s;
  1445.  *
  1446.  *  DESCRIPTION
  1447.  *
  1448.  *    Perror produces a message on the standard error stream which
  1449.  *    provides more information about the library or system error
  1450.  *    just encountered.  The argument string s is printed, followed
  1451.  *    by a ':', a blank, and then a message and a newline.
  1452.  *
  1453.  *    An undocumented feature of the unix perror is that if the string
  1454.  *    's' is a null string (NOT a NULL pointer!), then the ':' and
  1455.  *    blank are not printed.
  1456.  *
  1457.  *    This version just complains about an "unknown system error".
  1458.  *
  1459.  */
  1460.  
  1461. #ifndef unix
  1462. LOCAL VOID perror (s)
  1463. char *s;
  1464. {
  1465.     if (s && *s != EOS) {
  1466.     (VOID) fprintf (stderr, "%s: ", s);
  1467.     }
  1468.     (VOID) fprintf (stderr, "<unknown system error>\n");
  1469. }
  1470. #endif    /* !unix */
  1471.